package com.cw.starter.exception.handler;

import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import com.cw.starter.center.dto.R;
import com.cw.starter.common.util.IdGenerator;
import com.cw.starter.exception.BizException;
import com.cw.starter.exception.CheckException;
import com.cw.starter.exception.DBException;
import com.cw.starter.exception.SystemException;
import com.cw.starter.exception.TimeoutException;

/**
 * 异常捕获类
 *
 * @author chenw
 * @date 2021/7/27
 */
@RestControllerAdvice(basePackages = {"com.cw"})
public class ExceptionControllerHandler {
    private static final Logger logger = LoggerFactory.getLogger(ExceptionControllerHandler.class);

    @ExceptionHandler(value = {BindException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public R bindException(BindException bindException) {
        return R.fail(bindException.getBindingResult()
            .getAllErrors()
            .get(0)
            .getDefaultMessage(), R.FAIL, bindException.getBindingResult()
            .getAllErrors()
            .get(0)
            .getDefaultMessage());
    }

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public R handleException(Exception e) {
        return R.fail(e.getMessage());
    }

    /**
     * 针对{@link BizException}类型的异常，该异常一般为业务抛出的异常，主要用于向前台传输异常信息
     *
     * @param ex 抛出的异常
     * @author chenwei
     */
    @ExceptionHandler(BizException.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public R handlerError(BizException ex) {
        logger.error(ex.getErrorMessage(), ex);
        return R.builder()
            .idemSerialId(idemSerialId())
            .code(ex.getErrorCode())
            .msg(ex.getErrorMessage())
            .data(ex.getErrorMessage())
            .tradeDate(new Date())
            .build();

    }

    /**
     * 针对{@link CheckException}类型的异常，该异常一般为校验错误
     *
     * @param ex 抛出的异常
     * @author chenwei
     */
    @ExceptionHandler(CheckException.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public R handlerError(CheckException ex) {
        logger.error(ex.getMessage(), ex);
        return R.builder()
            .idemSerialId(idemSerialId())
            .code(ex.getErrorCode())
            .msg(ex.getMessage())
            .data(ex.getErrorMessage())
            .tradeDate(new Date())
            .build();
    }

    /**
     * 针对{@link DBException}类型的异常，该异常一般为数据库异常
     *
     * @param ex 抛出的异常
     * @author chenwei
     */
    @ExceptionHandler(DBException.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public R handlerError(DBException ex) {
        logger.error(ex.getErrorMessage(), ex);
        return R.builder()
            .idemSerialId(idemSerialId())
            .code(ex.getErrorCode())
            .msg(ex.getErrorMessage())
            .tradeDate(new Date())
            .data(ex.getErrorMessage())
            .build();
    }

    /**
     * 针对{@link TimeoutException}类型的异常，该异常一般为rpc超时异常
     *
     * @param ex 抛出的异常
     * @author chenwei
     */
    @ExceptionHandler(TimeoutException.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public R handlerError(TimeoutException ex) {
        logger.error(ex.getErrorMessage(), ex);
        return R.builder()
            .code(ex.getErrorCode())
            .msg(ex.getErrorMessage())
            .data(ex.getErrorMessage())
            .idemSerialId(idemSerialId())
            .tradeDate(new Date())
            .build();
    }

    /**
     * 除了基础类型之外的异常，该异常一般为内部实现出错时会抛出的异常，例如NullPointerException等等，非业务逻辑异常
     *
     * @param ex 抛出的异常
     * @author chenwei
     */
    @ExceptionHandler(SystemException.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public R handlerError(SystemException ex) {
        logger.error(ex.getErrorMessage(), ex);
        return R.builder()
            .code(ex.getErrorCode())
            .msg(ex.getErrorMessage())
            .data(ex.getErrorMessage())
            .idemSerialId(idemSerialId())
            .tradeDate(new Date())
            .build();
    }

    /**
     * 幂等流水号
     *
     * @return 幂等流水号
     */
    private Long idemSerialId() {
        return IdGenerator.nextId();
    }
}


